# versioning
BUMP_VERSION_CONFIG ?= .bumpversion.cfg
# allow missing "patch" number
BUMP_VERSION_PARSE_REGEX ?= (?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?
CURRENT_VERSION_FILE ?= VERSION

DIR_RELEASE_ARCHIVES ?= .
RELEASE_TAR_COMPRESSION_BIN = gzip
ifeq ($(RELEASE_TAR_COMPRESSION_BIN),gzip)
    RELEASE_TAR_COMPRESSION_EXTENSION = gz
else ifeq ($(RELEASE_TAR_COMPRESSION_BIN),bzip2)
    RELEASE_TAR_COMPRESSION_EXTENSION = bz2
else ifeq ($(RELEASE_TAR_COMPRESSION_BIN),xz)
    RELEASE_TAR_COMPRESSION_EXTENSION = xz
else ifeq ($(RELEASE_TAR_COMPRESSION_BIN),zstd)
    RELEASE_TAR_COMPRESSION_EXTENSION = zst
else
    $(error Invalid RELEASE_TAR_COMPRESSION specified: should be one of gzip / bzip2 / xz)
endif
RELEASE_TAR_FILENAME = $(DIR_RELEASE_ARCHIVES)/$(PROJECT_NAME)-$(call get_current_version).tar.$(RELEASE_TAR_COMPRESSION_EXTENSION)
RELEASE_TAR_SIGN_FILENAME = $(RELEASE_TAR_FILENAME).asc
RELEASE_TAR_HASH_FUNC ?= sha256sum
RELEASE_TAR_HASH_FILENAME = $(RELEASE_TAR_FILENAME).$(RELEASE_TAR_HASH_FUNC)

# We need to set an explicit `--distribution` argument for `debchange` in order to work around a
# weird Ubuntu-specific hack, which causes non-predictable distribution names.
# Users may override this by setting `DEB_RELEASE_DISTRIBUTION` to an empty string.
# See https://notabug.org/sumpfralle/makefilet/issues/38
DEB_RELEASE_DISTRIBUTION ?= unstable

# override the variable for selecting a specific gpg key
SIGNATURE_EMAIL ?=

IS_VERSION_WITH_BUMPVERSION = $(wildcard $(BUMP_VERSION_CONFIG))
IS_VERSION_WITH_GIT = $(shell git rev-parse --is-inside-work-tree >/dev/null 2>&1 && echo 1 || true)
IS_VERSION_WITH_DEB = $(wildcard debian/changelog)
# We only want to bump version numbers in a package.json (and package-lock.json) file if
#  * a package.json exists
#  * it actually contains a version number and is not just used for dependency tracking (`npm pkg get version` returns '{}' if no version is tracked)
IS_VERSION_WITH_NPM = $(shell if [ -e package.json ] && [ "$$(npm pkg get version 2>/dev/null || echo "{}")" != "{}" ]; then echo 1; fi)

# assemble the list of files to be committed during "make release-*"
BUMP_VERSION_FILES ?= $(BUMP_VERSION_CONFIG)
ifneq ($(IS_VERSION_WITH_BUMPVERSION),)
	BUMP_VERSION_FILES += $(shell sed -n 's/^\[bumpversion:file:\(.*\)\]$$/\1/p' "$(BUMP_VERSION_CONFIG)")
else
	BUMP_VERSION_FILES =
endif
ifneq ($(IS_VERSION_WITH_DEB),)
	BUMP_VERSION_FILES += debian/changelog
endif
ifneq ($(IS_VERSION_WITH_NPM),)
	BUMP_VERSION_FILES += package.json
	ifneq "$(wildcard package-lock.json)" ""
		BUMP_VERSION_FILES += package-lock.json
	endif
endif

define IS_RELEASE_CURRENT_COMMIT_TEST =
	[ "$$(git show -s --format=%H "v$(call get_current_version)^{commit}" --)" = "$$(git show -s --format=%H)" ]
endef

get_current_version = $(shell if ! cut -f 1-3 -d . "$(CURRENT_VERSION_FILE)" 2>/dev/null; then echo >&2 "Missing '$(CURRENT_VERSION_FILE)' file (see variable 'CURRENT_VERSION_FILE)'"; echo "unknown"; fi)


.PHONY: help
help: help-release

.PHONY: help-release
help-release:
	@echo "Release-related packaging targets:"
	@echo "    release-major"
	@echo "    release-minor"
	@echo "    release-patch"
	@echo "    release-undo"
	@echo "    release-push"
	@echo "    release-tar"
	@echo "    release-tar-sign"
	@echo

release-major: VERSION_BUMP=major
release-minor: VERSION_BUMP=minor
release-patch: VERSION_BUMP=patch

.PHONY: release-major release-minor release-patch
release-major release-minor release-patch: release-generic

.PHONY: release-generic
# prevent duplicate tags for successive commits
release-generic:: | release-check-current-commit-is-not-release
ifneq ($(IS_VERSION_WITH_BUMPVERSION),)
release-generic:: | release-bumpversion
endif
ifneq ($(IS_VERSION_WITH_DEB),)
release-generic:: | release-debchange
endif
# take care for the ordering: 'release-git' needs to run before '-debchange' and '-bumpversion'
ifneq ($(IS_VERSION_WITH_GIT),)
release-generic:: release-git
endif
ifneq ($(IS_VERSION_WITH_NPM),)
release-generic:: release-npm
# ensure that "release-git" is executed after "release-npm": package.json has to be updated in time
release-git: release-npm
endif

.PHONY: release-check-current-commit-is-not-release
release-check-current-commit-is-not-release:
	@$(IS_RELEASE_CURRENT_COMMIT_TEST) && { echo >&2 "ERROR: current commit is a release"; false; } || true

.PHONY: release-check-current-commit-is-release
release-check-current-commit-is-release:
	@$(IS_RELEASE_CURRENT_COMMIT_TEST) || { echo >&2 "ERROR: current commit is not a release"; false; }

.PHONY: release-bumpversion
release-bumpversion:
	@if [ ! -e "$(BUMP_VERSION_CONFIG)" ]; then \
		echo >&2 "Configuration file '$(BUMP_VERSION_CONFIG)' missing"; \
		exit 1; \
	fi
	@# bump version without committing (done after updating debian/changelog)
	@bumpversion --config-file "$(BUMP_VERSION_CONFIG)" \
		--parse "$(BUMP_VERSION_PARSE_REGEX)" \
		--current-version "$(call get_current_version)" \
		"$(VERSION_BUMP)"

.PHONY: release-debchange
release-debchange:
	debchange --newversion "$(call get_current_version)-1" "New upstream release"
	@# Append a named distribution, if one is specified.
	@# Read from /dev/null in order to prevent `debchange` from asking questions regarding the
	@# (potentially unsupported) distribution name.
	debchange --release ""$(patsubst %, --distribution %, $(DEB_RELEASE_DISTRIBUTION)) </dev/null

.PHONY: release-git
release-git:
	if [ -n "$(BUMP_VERSION_FILES)" ]; then git add $(BUMP_VERSION_FILES); fi
	@# add other version-related files
	if [ -n "$(BUMP_VERSION_COMMIT_FILES)" ]; then git add $(BUMP_VERSION_COMMIT_FILES); fi
	git commit -m "Bump version: $$(git show "HEAD:./$(CURRENT_VERSION_FILE)") -> $(call get_current_version)"
	git tag -a "v$(call get_current_version)" -m "Bump version: $(VERSION_BUMP)"

.PHONY: release-npm
release-npm:
	npm --no-git-tag-version version "$(call get_current_version)"

.PHONY: release-undo
release-undo: release-check-current-commit-is-release
	[ -z "$$(git status --short --untracked-files=no)" ]; # refuse to run in a non-clean working directory
	git tag -d "v$(call get_current_version)"
	git reset --hard HEAD~

.PHONY: release-push
release-push:
	git push --follow-tags

.PHONY: release-tar
release-tar: $(RELEASE_TAR_FILENAME) $(RELEASE_TAR_HASH_FILENAME)

$(RELEASE_TAR_FILENAME): release-check-current-commit-is-release
	git archive --prefix=$(PROJECT_NAME)-$(RELEASE)/ --format=tar HEAD \
		| "$(RELEASE_TAR_COMPRESSION_BIN)" > "$(RELEASE_TAR_FILENAME).tmp"
	mv "$(RELEASE_TAR_FILENAME).tmp" "$(RELEASE_TAR_FILENAME)"

$(RELEASE_TAR_HASH_FILENAME): $(RELEASE_TAR_FILENAME)
	$(RELEASE_TAR_HASH_FUNC) "$<" >"$@"

.PHONY: release-tar-sign
release-tar-sign: $(RELEASE_TAR_SIGN_FILENAME)

$(RELEASE_TAR_SIGN_FILENAME): $(RELEASE_TAR_FILENAME)
	gpg --armor --detach-sign --sign $(patsubst %,--local-user %,$(SIGNATURE_EMAIL)) "$<"
